package net.sourceforge.openclixml.environment;

import java.util.HashMap;


/**
 * An environemnt that uses Strings as keys and values. In Clixml this environment can be used to store XPath variables.
 * Keys and Values of the environment are Strings
 * @version $Revision: 47 $
 * @author dominikjungo
 *
 * Last changed: $Date: 2008-02-03 22:01:31 +0100 (Sun, 03 Feb 2008) $
 * The License for this file can be found in doc/Licenses/license-OpenCliXML.txt
 */
public class Environment extends HashMap<String, String> implements IEnvironment<String, String> {
    /**
     * The environemnt extended by this environment.
     */
    private Environment extended;

    /**
     * Creates an empty environment.
     */
    public Environment() {
    }

    /**
     * Creates an environment with one variable.
     * @param variableName The name of the variable contained in this environemnt
     * @param value The value of the variable contained in this environment
     */
    public Environment(final String variableName, final String value) {
        add(variableName, value);
    }

    /**
     * Creates an empty environment extending another environemnt. This extended environment will be used lateron to resolve variables probably not contained in this environment.
     * @param env The environment that will be extended by this newly created environment
     */
    private Environment(final Environment env) {
        extended = env;
    }

    /**
     * Creates an environment with one variable extending another environemnt. This extended environment will be used lateron to resolve variables probably not contained in this environment.
     * @param env The environment that will be extended by this newly created environment
     * @param variableName The name of the variable contained in this environemnt
     * @param value The value of the variable contained in this environment
     */
    private Environment(final Environment env, final String variableName,
        final String value) {
        this(env);
        add(variableName, value);
    }

    /**
     * {@inheritDoc}
     */
    public final void add(final String variableName, final String value) {
        put(variableName, value);
    }

    /**
     * {@inheritDoc}
     */
    public final IEnvironment<String, String> extend() {
        return new Environment(this);
    }

    /**
     * {@inheritDoc}
     */
    public final IEnvironment<String, String> extend(
        final String variableName, final String value) {
        return new Environment(this, variableName, value);
    }

    /**
     * {@inheritDoc}
     */
    public final String resolve(final String var)
        throws VariableNotFoundException {
        try {
            return resolveLocal(var);
        } catch (VariableNotFoundException exception) {
            return resolveDeeply(var);
        }
    }

    private String resolveLocal(final String var)
        throws VariableNotFoundException {
        String val = get(var);

        if (val == null) {
            throw new VariableNotFoundException(var);
        } else {
            return val;
        }
    }

    /**
     * Resolves the variable in the base environment.
     * @param var the name of the variable that needs to be resolved.
     * @return the value of the resolved variable
     *
     */
    private String resolveDeeply(final String var)
        throws VariableNotFoundException {
        if (extended != null) {
            return extended.resolve(var);
        } else {
            throw new VariableNotFoundException(var);
        }
    }

    /**
     * resolves an XPath expression in this environment.
     * @param xpath the xpath expression to be resolved.
     * @return the given xpath expression with replaced variables.
     */
    public final String resolveXPath(final String xpath) {
        int variableDecl = xpath.indexOf("$");

        if (variableDecl == -1) {
            return xpath;
        }

        int variableEnd = variableDecl +
            xpath.substring(variableDecl).indexOf("/");

        if (variableEnd == (variableDecl - 1)) {
            variableEnd = variableDecl + xpath.length();
        }

        String variableName = xpath.substring(variableDecl + 1, variableEnd);

        return resolveXPath(xpath.replaceAll("\\$" + variableName, "variable"));
    }
}
